/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package util;



/**
 *
 * La PileDePile sert à contenir les coups joués et à tester pour un plateau particulier,
 * à passer en paramêtre avant toute utilisation.
 * Le plateau doit également connaitre la pile (utiliser les setteurs prévu à cet effet).
 *
 * Seule les méthodes de la pile doivent être utilisé pour dérouler la partie.
 * C'est une structure en couche en fait.
 * Le plateau est utilisé pour les calcul d'heuristique divers.
 *
 * exemple d'algo utilisant cette classe:
 *
 * if (plateau.estFeuille()){
 *      return heuristique()          // /!\ces fonctions n'existent pas/!\ feuille: tester fin de partie/profondeur...
 * }else{
 *  for(int i= pile.branchement(); --i >= 0;){              //branchement() calcul les coups et renvoie leur nombre; la boucle permet de jouer tous les coups
 *      pile.jouerCoup(); //joue le premier coup dispo;
 *
 *          //insérer appel récursif et truc interressant ici
 *
 *      pile.dejouerCoup();// revient sur le coup joué
 *  }
 * }
 * }
 *
 * @author nlupins
 */
public abstract class PileDePile {

   public static final int N = 200, M = 750;// M majore le nbre de tour... N majore le nbre de coup moyen.
   private PlateauTablut p;
   private int[] ptrs  = new int[M];
   private byte[] pions = new byte[M*N];
   private byte[] Pdepart = new byte[M*N];
   private byte[] Parrive = new byte[M*N];
   private int nbCoup = 0;
   private byte PionBest;
   private byte DepartBest;
   private byte ArriveBest;

   /**
    * Constructeur.
    */
    public PileDePile() {
    }
    /**
     * Setteur du plateau.
     * @param p
     */
    public void setP(PlateauTablut p) {
        this.p = p;
    }

    /**
     * Nouvelle partie avec le meme (objet) plateau.
     */
    public final void reset(){
        p.init();
    }

    /**
     * Necessaire pour ajouter un coup à la pile. Appelé par le plateau au calcul des coups.
     * @param pion
     * @param depart
     * @param arrive
     */
    public final void addCoup(byte pion, byte depart, byte arrive){
        int ptr = ptrs[p.date]; //date incrémenté quand on joue
        pions[ptr] = pion;
        Pdepart[ptr] = depart;
        Parrive[ptr] = arrive;
        ptrs[p.date]++;
        nbCoup++;
    }


    /**
     * Joue le premier coup sur la pile.
     */
    public final void jouerCoup(){
        int ptr = ptrs[p.date];
        ptrs[p.date+1] = ptr+1;
        p.joue(pions[ptr], Pdepart[ptr], Parrive[ptr]);
    }

    /**
     * Revient sur le dernier coup joué.
     */
    public final void dejouerCoup(){
        int ptr = ptrs[p.date - 1];
        p.dejoue(pions[ptr], Pdepart[ptr], Parrive[ptr]); //décrémente p.date;
        ptrs[p.date]--;
    }

    /**
     * Effectue le calcul des coups jouables en effet de bord.
     * @return nbre de coups qui ont été calculés.
     */
    public int branchement(){
        nbCoup = 0;
        p.calcul_mvts();
        return nbCoup;
    }

    /**
     * Sauvegarde un coup particulier (le dernier joué), idéalement le meilleur.
     */
    public void setBest(){
        int ptr = ptrs[p.date - 1];
        PionBest = pions[ptr];
        DepartBest = Pdepart[ptr];
        ArriveBest = Parrive[ptr];
    }

    /**
     * Joue le coup spécialement sauvegardé.
     */
    public void jouerBest(){
        this.addCoup(PionBest, DepartBest, ArriveBest);
        jouerCoup();
    }

    /**
     * Joue ce coup là.
     * @param ligneDepart
     * @param colonneDepart
     * @param ligneArrive
     * @param ColonneArrive
     */
    public void jouerCeCoup(int ligneDepart, int colonneDepart, int ligneArrive, int colonneArrive){
        byte depart = PlateauTablut.coord( ligneDepart , colonneDepart);
        byte arrive = PlateauTablut.coord( ligneArrive , colonneArrive);
        p.jouerCeCoup(depart, arrive);
    }

}
